﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using AI.Fuzzy.Library;


namespace TAI
{
    public class MotorInferencia
    {
        public int nivelActual = 4;
        public int contador =  0;
        int preguntas = 5;
        public int nivel = 0;
        
        public MamdaniFuzzySystem _sistemaDifuso = null;
        public BaseHechos hechos = new BaseHechos();

        MamdaniFuzzySystem CreateSystem()
        {
            /* Crea un sistema difuso vacío*/

            MamdaniFuzzySystem sistemaDifuso = new MamdaniFuzzySystem();
            double p = 0.5;

            /* Crea las variables de entradas con sus términos*/

            FuzzyVariable desempeno = new FuzzyVariable("desempeno", 0, 5);

            desempeno.Terms.Add(new FuzzyTerm("muyBueno", new TrapezoidMembershipFunction(p * 7, p * 8, (p * 8) + 1, (p * 8) + 1)));
            desempeno.Terms.Add(new FuzzyTerm("bueno", new TriangularMembershipFunction(p * 5, (((p * 8) - (p * 5)) / 2) + (p * 5), p * 8)));
            desempeno.Terms.Add(new FuzzyTerm("normal", new TriangularMembershipFunction(p * 3, (((p * 6) - (p * 3)) / 2) + (p * 3), p * 6)));
            desempeno.Terms.Add(new FuzzyTerm("malo", new TriangularMembershipFunction(p, (((p * 4) - p) / 2) + p, p * 4)));
            desempeno.Terms.Add(new FuzzyTerm("muyMalo", new TrapezoidMembershipFunction(0, 0, p, p * 2)));

            sistemaDifuso.Input.Add(desempeno);

            FuzzyVariable tiempo = new FuzzyVariable("tiempo", 0, 320);

            tiempo.Terms.Add(new FuzzyTerm("muyLento", 
                new TrapezoidMembershipFunction(280, 320, 360, 360)));
            tiempo.Terms.Add(new FuzzyTerm("lento", 
                new TriangularMembershipFunction(200, 260, 320)));
            tiempo.Terms.Add(new FuzzyTerm("normal", 
                new TriangularMembershipFunction(120, 180, 240)));
            tiempo.Terms.Add(new FuzzyTerm("rapido", 
                new TriangularMembershipFunction(40, 100, 160)));
            tiempo.Terms.Add(new FuzzyTerm("muyRapido", 
                new TrapezoidMembershipFunction(0, 0, 40, 80)));

            sistemaDifuso.Input.Add(tiempo);

            /* Crea la variable de salida con sus términos */

            FuzzyVariable nivel = new FuzzyVariable("nivel", -1, 1);
            nivel.Terms.Add(new FuzzyTerm("bajar", new TriangularMembershipFunction(-1.5, -1, -0.5)));
            nivel.Terms.Add(new FuzzyTerm("mantener", new TriangularMembershipFunction(-0.5, 0, 0.5)));
            nivel.Terms.Add(new FuzzyTerm("subir", new TriangularMembershipFunction(0.5, 1, 1.5)));
            sistemaDifuso.Output.Add(nivel);

            /* Crea las reglas difusas */

            try
            {
                MamdaniFuzzyRule rule1 = sistemaDifuso.ParseRule("if (tiempo is muyRapido) and (desempeno is muyBueno) then nivel is subir");
                MamdaniFuzzyRule rule2 = sistemaDifuso.ParseRule("if (tiempo is muyRapido) and (desempeno is bueno) then nivel is subir");
                MamdaniFuzzyRule rule3 = sistemaDifuso.ParseRule("if (tiempo is muyRapido) and (desempeno is normal) then nivel is mantener");
                MamdaniFuzzyRule rule4 = sistemaDifuso.ParseRule("if (tiempo is muyRapido) and (desempeno is malo) then nivel is bajar");
                MamdaniFuzzyRule rule5 = sistemaDifuso.ParseRule("if (tiempo is muyRapido) and (desempeno is muyMalo) then nivel is bajar");

                MamdaniFuzzyRule rule6 = sistemaDifuso.ParseRule("if (tiempo is rapido) and (desempeno is muyBueno) then nivel is subir");
                MamdaniFuzzyRule rule7 = sistemaDifuso.ParseRule("if (tiempo is rapido) and (desempeno is bueno) then nivel is subir");
                MamdaniFuzzyRule rule8 = sistemaDifuso.ParseRule("if (tiempo is rapido) and (desempeno is normal) then nivel is mantener");
                MamdaniFuzzyRule rule9 = sistemaDifuso.ParseRule("if (tiempo is rapido) and (desempeno is malo) then nivel is bajar");
                MamdaniFuzzyRule rule10 = sistemaDifuso.ParseRule("if (tiempo is rapido) and (desempeno is muyMalo) then nivel is bajar");

                MamdaniFuzzyRule rule11 = sistemaDifuso.ParseRule("if (tiempo is normal) and (desempeno is muyBueno) then nivel is subir");
                MamdaniFuzzyRule rule12 = sistemaDifuso.ParseRule("if (tiempo is normal) and (desempeno is bueno) then nivel is subir");
                MamdaniFuzzyRule rule13 = sistemaDifuso.ParseRule("if (tiempo is normal) and (desempeno is normal) then nivel is mantener");
                MamdaniFuzzyRule rule14 = sistemaDifuso.ParseRule("if (tiempo is normal) and (desempeno is malo) then nivel is bajar");
                MamdaniFuzzyRule rule15 = sistemaDifuso.ParseRule("if (tiempo is normal) and (desempeno is muyMalo) then nivel is bajar");

                MamdaniFuzzyRule rule16 = sistemaDifuso.ParseRule("if (tiempo is lento) and (desempeno is muyBueno) then nivel is subir");
                MamdaniFuzzyRule rule17 = sistemaDifuso.ParseRule("if (tiempo is lento) and (desempeno is bueno) then nivel is subir");
                MamdaniFuzzyRule rule18 = sistemaDifuso.ParseRule("if (tiempo is lento) and (desempeno is normal) then nivel is mantener");
                MamdaniFuzzyRule rule19 = sistemaDifuso.ParseRule("if (tiempo is lento) and (desempeno is malo) then nivel is bajar");
                MamdaniFuzzyRule rule20 = sistemaDifuso.ParseRule("if (tiempo is lento) and (desempeno is muyMalo) then nivel is bajar");

                MamdaniFuzzyRule rule21 = sistemaDifuso.ParseRule("if (tiempo is muyLento) and (desempeno is muyBueno) then nivel is mantener");
                MamdaniFuzzyRule rule22 = sistemaDifuso.ParseRule("if (tiempo is muyLento) and (desempeno is bueno) then nivel is mantener");
                MamdaniFuzzyRule rule23 = sistemaDifuso.ParseRule("if (tiempo is muyLento) and (desempeno is normal) then nivel is bajar");
                MamdaniFuzzyRule rule24 = sistemaDifuso.ParseRule("if (tiempo is muyLento) and (desempeno is malo) then nivel is bajar");
                MamdaniFuzzyRule rule25 = sistemaDifuso.ParseRule("if (tiempo is muyLento) and (desempeno is muyMalo) then nivel is bajar");

                sistemaDifuso.Rules.Add(rule1);
                sistemaDifuso.Rules.Add(rule2);
                sistemaDifuso.Rules.Add(rule3);
                sistemaDifuso.Rules.Add(rule4);
                sistemaDifuso.Rules.Add(rule5);
                sistemaDifuso.Rules.Add(rule6);
                sistemaDifuso.Rules.Add(rule7);
                sistemaDifuso.Rules.Add(rule8);
                sistemaDifuso.Rules.Add(rule9);
                sistemaDifuso.Rules.Add(rule10);
                sistemaDifuso.Rules.Add(rule11);
                sistemaDifuso.Rules.Add(rule12);
                sistemaDifuso.Rules.Add(rule13);
                sistemaDifuso.Rules.Add(rule14);
                sistemaDifuso.Rules.Add(rule15);
                sistemaDifuso.Rules.Add(rule16);
                sistemaDifuso.Rules.Add(rule17);
                sistemaDifuso.Rules.Add(rule18);
                sistemaDifuso.Rules.Add(rule19);
                sistemaDifuso.Rules.Add(rule20);
                sistemaDifuso.Rules.Add(rule21);
                sistemaDifuso.Rules.Add(rule22);
                sistemaDifuso.Rules.Add(rule23);
                sistemaDifuso.Rules.Add(rule24);
                sistemaDifuso.Rules.Add(rule25);

            }

            catch (Exception ex)
            {
                //MessageBox.Show(string.Format("Parsing exception: {0}", ex.Message));
                return null;
            }

            return sistemaDifuso;
        }


        public int getNivel()
        {
            nivel = 0;
                               
            if (contador == preguntas)
            {
                contador = 0;
                _sistemaDifuso = CreateSystem();

                /* Obtiene las variable del sistema de lógica difusa (para mejor comprensión) */

                FuzzyVariable fvDesempeno = _sistemaDifuso.InputByName("desempeno");
                FuzzyVariable fvTiempo = _sistemaDifuso.InputByName("tiempo");
                FuzzyVariable fvNivel = _sistemaDifuso.OutputByName("nivel");

                /* Asocial valore de entrada con las variables de entrada */

                Dictionary<FuzzyVariable, double> valoresEntrada = new Dictionary<FuzzyVariable, double>();

                Tuple<double, double> variables = hechos.deme();
                double d = variables.Item1;
                double t = variables.Item1;                
                
                valoresEntrada.Add(fvDesempeno, d);
                valoresEntrada.Add(fvTiempo, t);

                /* Calcula el resultado: un valor de salida por cada variable de salida */

                Dictionary<FuzzyVariable, double> resultado = _sistemaDifuso.Calculate(valoresEntrada);

                /* Obtiene el valor de salida de la variable fvNivel */

                double r = resultado[fvNivel];

                if(r<-0.5){
                    r = -1; 
                }else{
                    if(-0.5 < r && r < 0.5){
                        r = 0;
                    }else{
                        r = 1;
                    }
                }

                nivel += (int)r;
                hechos.agregar(nivelActual+nivel);

            }
            contador++;
            nivelActual += nivel ;
            return nivelActual;
        }

        public void agregar(double d, double t) {
            hechos.agregar(d, t);
        }


        public bool condicionParada() {
            bool parada = false;
            if (hechos.cantidadRespuestas() == 30)
            {
                parada = true;
            }
            else
            {
                if ((nivelActual == 7) && (nivel == -1))
                {
                    parada = true;
                }
                else
                {
                    if (nivelActual <= 1 && nivel == -1)
                    {
                        parada = true;
                    }
                }
            }
            return parada;
            }
        

    }
}